#include "list.h"

LIST *creatlist() //创建链表
{
    return calloc(sizeof(LIST), 1); 
}

int  addlist(LIST *ls,int id,char *src,char *data) //添加链表元素
{
    LIST *p = ls; 
    while(p->next) 
    {
        p = p->next;
    }   
    LIST *tmp = creatlist();

    tmp->next = p->next;  
    p->next = tmp;   //插入到链表的最后位置
    tmp->id = id;   //将信息存储到该结点中
    strcpy(tmp->src,src);
    strcpy(tmp->data,data);

    return 0;
}
int  changelist(LIST *ls, int id, char *src, char *dest, char *data) //修改链表元素内容
{
    LIST *tmp = ls;

    tmp->id = id;
    strcpy(tmp->src, src);
    strcpy(tmp->dest, dest);
    strcpy(tmp->data, data);

    return 0;
}
void showlist(LIST *ls) //遍历链表，打印出来
{
    LIST *p = ls;
    while(p) 
    {
        printf("Clients logged into the server have %s\n", p->src);
        p = p->next;
    }
}

int dellist(LIST *login, char *src) //删除节点
{
    LIST *ls = login;
    while(strcmp(ls->next->src, src))
        ls = ls->next;
    
    if(ls->next->next == NULL)
        ls->next = NULL;
    else 
    {
        ls->next = ls->next->next;
    }   

        return 0;
}       
int findlist(LIST *ls, char *name)  //查找链表中是否有寻呼的用户
{
    showlist(ls);
    LIST *login = ls;
    while(login) 
    {
        if((strcmp(login->src, name)) == 0)
            return 0;   //存在，返回0
        login=login->next;
    }
    return -1;  //不存在返回-1
}

int resolve(int id, LIST *usrinfo, LIST *loginlist)   //解析服务器接收到的数据
{
    int fdclient;
    LIST *ls=loginlist;
    char buff[512];
    char *name=usrinfo->src;

    switch (id) //根据数据包的id处理
    {
        case 1: //登录信息
            addlist(loginlist,usrinfo->id, usrinfo->src, usrinfo->data);  //在链表中添加一个元素
            if((mkfifo(usrinfo->src, 0644)) < 0)  //创建服务器与自身客户端之间通信的管道
                perror("mkfifo");
            break;

        case 2: //聊天包    
            if(strlen(usrinfo->dest)!=0)   //单发
            {
                if(findlist(loginlist,usrinfo->dest)<0)
                {
                    printf("The user is not login\n");
                    if((fdclient=open(usrinfo->src,O_WRONLY))<0)  //打开发送者的客户端
                        perror("open userfifo");
                    write(fdclient,"Don't have this user\n",21);   //如果没有告知发送者没有该用户
                    close(fdclient);
                    return -1;
                }
                char buff[512]={0};
                if((fdclient=open(usrinfo->dest,O_WRONLY))<0)  //打开接收者的客户端
                    perror("open userfifo");
                sprintf(buff,"%s:%s\n",usrinfo->src,usrinfo->data);
                if((write(fdclient,buff,strlen(buff)))<0)    //将消息写入
                    perror("write userfifo");
                close(fdclient);
            }
            else   //群发
            {               
                sprintf(buff,"%s:%s\n",usrinfo->src,usrinfo->data);  //“发送者:消息”格式写入buff
                while(ls)   //遍历链表
                {
                    if((strcmp(ls->src, name)) != 0)  //若不是发送者的客户端，打开并发送消息
                    {
                        fdclient=open(ls->src,O_WRONLY);
                        write(fdclient,buff,strlen(buff));
                        close(fdclient);
                    }
                    ls=ls->next;
                }
            }
            break;
                        
        case 3: //退出
            unlink(usrinfo->src);    //退出，把用户的管道文件删除
            dellist(loginlist, usrinfo->src);   //删除链表元素
            usrinfo->id = 0; //修改协议号，保证下次在没有用户输入的情况下也不会重复删除，导致段错误
            break;
    }
    return 0;
}

int  commclient(LIST *usrinfo, int fdserver)   //用户输入数据并把它封装到结构体中去
{
    char buff[512] = {0};
    char *destname;
    int flags;
    int len;

    char name[20];
    char data[512];
    int i=0,j=0;

    if((len = read(STDIN_FILENO, buff, sizeof(buff))) < 0) 
    { //读用户输入的数据,小于零说用有错误
        if(errno != EAGAIN)
            perror("read user input\n");
    }
    else  
    {   //如果len不小于0，说明有数据，如果有数据就把数据封装到结构体中去
        if((strcmp(buff, "quit\n")) == 0||(strcmp(buff,"exit\n") == 0)) 
        { //如果用户输入quit或exit就向服务器发送一个id为3的包，是退出登录信息
            usrinfo->id = 3; //把id改为3
            write(fdserver, usrinfo, sizeof(LIST)); //给服务器发送信息
            return 0;
        }

        while(buff[i]!='\0'&&buff[i]!=':')  //如果是“客户端名:消息”的格式，表示单发
        {
            name[i]=buff[i];
            ++i;
        }
        name[i]='\0';
        if(buff[i]==':')
            ++i;
        else       //其他情况视为群发
        {
            i=0;
            name[0]='\0';
        }
        while (buff[i]!='\0')
        {
            data[j++]=buff[i++];
        }
        data[j]='\0';
        usrinfo->id=2;   //对消息进行封装
        strcpy(usrinfo->dest,name);
        strcpy(usrinfo->data,data);
        char *temp="all client";
        if(strlen(name)!=0)   //单发打印提示
            printf("usrinfo->src = %s, usrinfo->dest = %s, usrinfo->data = %s\n", usrinfo->src, usrinfo->dest, usrinfo->data);
        else    //群发打印提示
            printf("usrinfo->src = %s, usrinfo->dest = %s, usrinfo->data = %s\n", usrinfo->src, temp, usrinfo->data);
        write(fdserver,usrinfo,sizeof(LIST));
    }
    return 0;
}

void readclient(char *clientfifo)   //读取自己管道文件中服务器发给自己的文件
{
    int len;
    char buff[512] = {0};
    LIST *tmp = creatlist();
    int fdclient;

    if((fdclient = open(clientfifo,O_RDONLY|O_NONBLOCK)) < 0)   //打开以自己名字命名的管道文件
        perror("open userfifo\n");
    if((len = read(fdclient, buff, sizeof(buff))) < 0)     //读管道中的数据
    {
        if(errno != EAGAIN)
            perror("read data\n");
    }
    else
        write(STDIN_FILENO, buff, strlen(buff));    //打印到屏幕上
    return;
}

void benoblock(int fd) //把文件设置为非阻塞
{
    int flags;
    if((flags = fcntl(fd, F_GETFL)) < 0)
        perror("get failed\n");
    flags |= O_NONBLOCK;
    if((fcntl(fd, F_SETFL, flags)) < 0)
        perror("set failed\n");
}